#ifndef _SQCLASS_H_
#define _SQCLASS_H_

struct SQInstance;

struct SQClassMember {
  SQClassMember() {}
  SQClassMember( const SQClassMember &o ) {
    val = o.val;
    attrs = o.attrs;
  }
  SQObjectPtr val;
  SQObjectPtr attrs;
};

typedef sqvector<SQClassMember> SQClassMemberVec;

#define MEMBER_TYPE_METHOD 0x01000000
#define MEMBER_TYPE_FIELD 0x02000000

#define _ismethod(o) (_integer(o)&MEMBER_TYPE_METHOD)
#define _isfield(o) (_integer(o)&MEMBER_TYPE_FIELD)
#define _make_method_idx(i) ((SQInteger)(MEMBER_TYPE_METHOD|i))
#define _make_field_idx(i) ((SQInteger)(MEMBER_TYPE_FIELD|i))
#define _member_type(o) (_integer(o)&0xFF000000)
#define _member_idx(o) (_integer(o)&0x00FFFFFF)

struct SQClass : public CHAINABLE_OBJ {
    SQClass( SQSharedState *ss, SQClass *base );
  public:
    static SQClass* Create( SQSharedState *ss, SQClass *base ) {
      SQClass *newclass = ( SQClass * )SQ_MALLOC( sizeof( SQClass ) );
      new( newclass ) SQClass( ss, base );
      return newclass;
    }
    ~SQClass();
    bool NewSlot( SQSharedState *ss, const SQObjectPtr &key, const SQObjectPtr &val, bool bstatic );
    bool Get( const SQObjectPtr &key, SQObjectPtr &val ) {
      if( _members->Get( key, val ) ) {
        if( _isfield( val ) ) {
          SQObjectPtr &o = _defaultvalues[_member_idx( val )].val;
          val = _realval( o );
        } else
        { val = _methods[_member_idx( val )].val; }
        return true;
      }
      return false;
    }
    bool SetAttributes( const SQObjectPtr &key, const SQObjectPtr &val );
    bool GetAttributes( const SQObjectPtr &key, SQObjectPtr &outval );
    void Lock() { _locked = true; if( _base ) _base->Lock(); }
    void Release() {
      if( _hook ) {
        _hook( _typetag, 0 );
      }
      sq_delete( this, SQClass );
    }
    void Finalize();
    #ifndef NO_GARBAGE_COLLECTOR
    void Mark( SQCollectable ** );
    #endif
    SQInteger Next( const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval );
    SQInstance *CreateInstance();
    SQTable *_members;
    SQClass *_base;
    SQClassMemberVec _defaultvalues;
    SQClassMemberVec _methods;
    SQObjectPtrVec _metamethods;
    SQObjectPtr _attributes;
    SQUserPointer _typetag;
    SQRELEASEHOOK _hook;
    bool _locked;
    SQInteger _udsize;
};

#define calcinstancesize(_theclass_) \
  (_theclass_->_udsize + sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))

struct SQInstance : public SQDelegable {
    void Init( SQSharedState *ss );
    SQInstance( SQSharedState *ss, SQClass *c, SQInteger memsize );
    SQInstance( SQSharedState *ss, SQInstance *c, SQInteger memsize );
  public:
    static SQInstance* Create( SQSharedState *ss, SQClass *theclass ) {
      SQInteger size = calcinstancesize( theclass );
      SQInstance *newinst = ( SQInstance * )SQ_MALLOC( size );
      new( newinst ) SQInstance( ss, theclass, size );
      if( theclass->_udsize ) {
        newinst->_userpointer = ( ( unsigned char * )newinst ) + ( size - theclass->_udsize );
      }
      return newinst;
    }
    SQInstance *Clone( SQSharedState *ss ) {
      SQInteger size = calcinstancesize( _class );
      SQInstance *newinst = ( SQInstance * )SQ_MALLOC( size );
      new( newinst ) SQInstance( ss, this, size );
      if( _class->_udsize ) {
        newinst->_userpointer = ( ( unsigned char * )newinst ) + ( size - _class->_udsize );
      }
      return newinst;
    }
    ~SQInstance();
    bool Get( const SQObjectPtr &key, SQObjectPtr &val )  {
      if( _class->_members->Get( key, val ) ) {
        if( _isfield( val ) ) {
          SQObjectPtr &o = _values[_member_idx( val )];
          val = _realval( o );
        } else
        { val = _class->_methods[_member_idx( val )].val; }
        return true;
      }
      return false;
    }
    bool Set( const SQObjectPtr &key, const SQObjectPtr &val ) {
      SQObjectPtr idx;
      if( _class->_members->Get( key, idx ) && _isfield( idx ) ) {
        _values[_member_idx( idx )] = val;
        return true;
      }
      return false;
    }
    void Release() {
      _uiRef++;
      if( _hook ) {
        _hook( _userpointer, 0 );
      }
      _uiRef--;
      if( _uiRef > 0 ) {
        return;
      }
      SQInteger size = _memsize;
      this->~SQInstance();
      SQ_FREE( this, size );
    }
    void Finalize();
    #ifndef NO_GARBAGE_COLLECTOR
    void Mark( SQCollectable ** );
    #endif
    bool InstanceOf( SQClass *trg );
    bool GetMetaMethod( SQVM *v, SQMetaMethod mm, SQObjectPtr &res );

    SQClass *_class;
    SQUserPointer _userpointer;
    SQRELEASEHOOK _hook;
    SQInteger _memsize;
    SQObjectPtr _values[1];
};

#endif
